from rest_framework import serializers
from apps.goods.models import SKU, GoodsCategory, SPU, \
    SPUSpecification, SpecificationOption, SKUSpecification
from celery_tasks.html.tasks import generate_static_sku_detail_html
from django.db import transaction


# sku 对应的规格序列化器
class SKUSpecificationSerializer(serializers.ModelSerializer):
    # 添加字段规格的id
    spec_id = serializers.IntegerField()
    # 添加字段规格选项的id
    option_id = serializers.IntegerField()

    class Meta:
        model = SKUSpecification
        fields = ['spec_id', 'option_id']


# 查询所有的sku序列化器
class SKUSerializer(serializers.ModelSerializer):
    """
    1. spu和category 主要是为了列表中显示
    2. 前端传递的是 spu_id 和 category_id 所以 我们定义以下2个字段
    3. 我们还没有接收前端的 spu_id 和 category_id 呢？
        针对于问题3 第二部解决
    """
    # 这里的StringRelatedField相当于转换了一下就返回给了前端展示
    spu = serializers.StringRelatedField(read_only=True)
    category = serializers.StringRelatedField(read_only=True)
    """
    我们用spu_id 接收前端的数据
    spu_id 是对应数据表中的spu_id 我们这么写是可以数据入库的
    """
    # 这里的IntegerField是为了添加spu_id字段和保存数据到数据库
    spu_id = serializers.IntegerField()
    category_id = serializers.IntegerField()
    # 保存规格选项是多个数据的列表，系统不支持
    specs = SKUSpecificationSerializer(many=True)

    class Meta:
        model = SKU
        fields = '__all__'

    # 重写create使他可以存储列表数据
    def create(self, validated_data):
        # 1. 先把嵌套的数据 pop出来
        # 因为 我们要获取列表数据 另外列表数据在 validated_data 也没有用[{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
        # 先剔除不用的数据
        specs_data = validated_data.pop('specs')

        with transaction.atomic():
            # ------- ① 事务开始的记录点 -------
            save_point = transaction.savepoint()
            try:
                # 2. 调用模型的保存方法 只不过保存的模型 没有保存规格和规格选项
                sku = SKU.objects.create(**validated_data)
                # 3. 我们自己实现 规格和规格选项
                for a in specs_data:
                    # 修改多字段，在下面添加。item  --> {spec_id: "4", option_id: 8}
                    SKUSpecification.objects.create(
                        sku=sku,
                        spec_id=a.get('spec_id'),
                        option_id=a.get('option_id')
                    )
            except Exception:
                # --------- ② 事务回滚点 --------
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError('未保存上数据')
            else:
                # -------- ③ 事务提交 ---------
                transaction.savepoint_commit(save_point)

                # 生成 html静态页面
                # 1.主要的工作是保存sku
                # 2.静态页面生成比较耗时
                # 这样执行 celery是没有问题的，我们的数据有问题
                sku.default_image = 'group1/M00/00/02/wKgDEl5KdqSEJ1v4AAAAAObL0Uk3728832'
                sku.save()
                # 调用生成静态化之前 设置默认图片
                generate_static_sku_detail_html.delay(sku.id)

                return sku

    def update(self, instance, validated_data):
        # 1.获取列表数据
        specs = validated_data.pop('specs')

        with transaction.atomic():
            # --开启事务--
            save_point = transaction.savepoint()
            try:
                # 2.原有的更新 该怎么更新还继续更新
                # 方案一： 调用父类 ，去实现没有问题的数据更新
                super().update(instance, validated_data)
                # 方案二： 把父类的源码 拿来
                # for attr, value in validated_data.items():
                #     setattr(instance, attr, value)
                # instance.save()
                # 3.遍历 实现列表数据的更新
                # [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
                for i in specs:
                    # item  {spec_id: "4", option_id: 8}
                    # 我们更新数据的时候 是更新他的值
                    SKUSpecification.objects.filter(sku=instance, spec_id=i.get('spec_id')
                                                    ).update(option_id=i.get('option_id'))
            except Exception as e:
                # --事物回滚，捕获异常--
                transaction.savepoint_rollback(save_point)
                return serializers.ValidationError('保存数据错误')
            else:
                # -- 事务保存 --
                transaction.savepoint_commit(save_point)
                return instance


# 获取三级分类的
class ThreeCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = ['id', 'name']


# 获取spu数据
class SimpleSPUSerializer(serializers.ModelSerializer):
    class Meta:
        model = SPU
        fields = ['id', 'name']


# 获取spu的规格选项
class SpecificationOptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = SpecificationOption
        fields = ['id', 'value']


class SPUSpecsSerializer(serializers.ModelSerializer):
    # 通过隐藏字段关联对象
    options = SpecificationOptionSerializer(read_only=True, many=True)

    class Meta:
        model = SPUSpecification
        fields = '__all__'
